/*******************************************************************************
 * Copyright(c) 2023 Levetop Semiconductor Co.,Led. All rights reserved.
 * @file     sd_spi.c
 * @author   UartTFT Application Team
 * @version  V0.0.1
 * @date     2023-01-01
 * @brief
 ********************************************************************************/

#include "sd_spi.h"
#include "ff.h"
#include "debug.h"
#include "delay.h"
#include "function_drv.h"

uint8_t DFF = 0xFF;
uint8_t SD_TYPE = 0x00;
uint32_t SD_cyc = 64;

MSD_CARDINFO SD0_CardInfo;

// cs pin
void SD_CS(uint8_t p)
{
	if (p == 0)
	{
		SD_CS_SET;
	}
	else
	{
		SD_CS_RESET;
	}
}

// Send command and release after sending
int32_t SD_sendcmd(uint8_t cmd, uint32_t arg, uint8_t crc)
{
	uint8_t r1;
	uint8_t retry;
	uint32_t time = 0xFFFF;

	SD_CS(0);
	//	HAL_Delay(20);     //20ms
	DelayMS(1);
	//	SD_SPIx_ReadWriteByte(DFF);		/* delayed */

	SD_CS(1);
	do
	{
		//									printf("DelayMS 20  SD_sendcmd   do\r\n" );
		retry = SD_SPIx_ReadWriteByte(DFF);
		DelayUS(1);
	} while ((retry != 0xFF) && (time-- > 0));
	if (time == 0)
	{
		SD_CS(0);
		SD_SPIx_ReadWriteByte(DFF);
		return 0xFF;
	}

	SD_SPIx_ReadWriteByte(cmd | 0x40);
	SD_SPIx_ReadWriteByte(arg >> 24);
	SD_SPIx_ReadWriteByte(arg >> 16);
	SD_SPIx_ReadWriteByte(arg >> 8);
	SD_SPIx_ReadWriteByte(arg);
	SD_SPIx_ReadWriteByte(crc);
	if (cmd == CMD12)
		SD_SPIx_ReadWriteByte(DFF);
	time = 0x0FFF;
	do
	{
		//									printf("DelayMS 20  SD_sendcmd   do 222  \r\n" );
		r1 = SD_SPIx_ReadWriteByte(0xFF);
		//		DelayUS(1);
	} while ((r1 & 0X80) && (time-- > 0));
	if (time == 0)
		return 1;

	return r1;
}

// Read data of specified length
uint8_t SD_ReceiveData(uint8_t *data, uint16_t len)
{
	uint8_t r1;
	uint32_t time = 0xFFFF;
	SD_CS(1);
	do
	{
		r1 = SD_SPIx_ReadWriteByte(0xFF);
		//      HAL_Delay(100);
		DelayUS(1);
	} while ((r1 != 0xFE) && (time-- > 0));
	if (time == 0)
		return 1;

	while (len--)
	{
		*data = SD_SPIx_ReadWriteByte(0xFF);
		data++;
	}
	SD_SPIx_ReadWriteByte(0xFF);
	SD_SPIx_ReadWriteByte(0xFF);
	return 0;
}

// Write a packet of 512 bytes to the sd card
uint8_t SD_SendBlock(uint8_t *buf, uint8_t cmd)
{
	uint16_t t;
	uint8_t r1;
	uint32_t time = 0xFFFF;

	do
	{
		//									printf("DelayMS   SD_SendBlock   do\r\n" );
		r1 = SD_SPIx_ReadWriteByte(0xFF);
		DelayUS(1);
	} while ((r1 != 0xFF) && (time-- > 0));
	if (time == 0)
		return 1;

	SD_SPIx_ReadWriteByte(cmd);
	if (cmd != 0XFD) // Not an end instruction
	{
		for (t = 0; t < 512; t++)
			SD_SPIx_ReadWriteByte(buf[t]); // Increase speed and reduce function parameter transfer time
		SD_SPIx_ReadWriteByte(0xFF);	   // Ignore crc
		SD_SPIx_ReadWriteByte(0xFF);
		t = SD_SPIx_ReadWriteByte(0xFF); // Receive response
		if ((t & 0x1F) != 0x05)
			return 2; // Response error
	}
	return 0; // Write succeeded
}

/////////////////////////////////////////////////////////////
// SD card initialization
////////////////////////////////////////////////////////////
uint8_t SD_init(void)
{
	uint8_t r1;
	uint8_t buff[6] = {0};
	uint16_t retry;
	uint8_t i;

	//	MX_SPI3_Init();
	SD_SPIx_Initial();
	SD_SPIx_SetSpeed(SPI_BaudRatePrescaler_256);

	SD_CS(0);
	for (retry = 0; retry < 10; retry++)
	{
		SD_SPIx_ReadWriteByte(DFF);
	}

	// SD card enters IDLE state
	retry = 10;
	do
	{
		r1 = SD_sendcmd(CMD0, 0, 0x95);
	} while ((r1 != 0x01) && (retry--));
	if (r1 != 0x01)
		return 1;

	// View the type of SD card
	SD_TYPE = 0;
	r1 = SD_sendcmd(CMD8, 0x1AA, 0x87);
	if (r1 == 0x01)
	{
		for (i = 0; i < 4; i++)
			buff[i] = SD_SPIx_ReadWriteByte(DFF); // Get trailing return value of R7 resp
		if (buff[2] == 0X01 && buff[3] == 0XAA)	  // Does the card support 2.7~3.6V
		{
			retry = 0xFF;
			do
			{
				SD_sendcmd(CMD55, 0, 0X01);				  // Send CMD55
				r1 = SD_sendcmd(CMD41, 0x40000000, 0X01); // Send CMD41
			} while (r1 && retry--);
			if (retry && SD_sendcmd(CMD58, 0, 0X01) == 0) // Start to identify SD2.0 card version
			{
				for (i = 0; i < 4; i++)
					buff[i] = SD_SPIx_ReadWriteByte(0XFF); // Get OCR value
				if (buff[0] & 0x40)
				{
					SD_TYPE = V2HC;
//					printf("SD_TYPE: V2HC \r\n");
				}
				else
				{
					SD_TYPE = V2;
//					printf("SD_TYPE: V2   \r\n");
				}
			}
		}
		else
		{
			SD_sendcmd(CMD55, 0, 0X01);		 // Send CMD55
			r1 = SD_sendcmd(CMD41, 0, 0X01); // Send CMD41
			if (r1 <= 1)
			{
				SD_TYPE = V1;
//				printf("SD_TYPE: V1   \r\n");
				retry = 0xFF;
				do // Wait to exit IDLE mode
				{
					SD_sendcmd(CMD55, 0, 0X01);		 // Send CMD55
					r1 = SD_sendcmd(CMD41, 0, 0X01); // Send CMD41
				} while (r1 && retry--);
			}
			else // MMC card does not support CMD55+CMD41 recognition
			{
				SD_TYPE = MMC; // MMC V3
//				printf("SD_TYPE: MMC   \r\n");
				retry = 0xFF;
				do // Wait to exit IDLE mode
				{
					r1 = SD_sendcmd(CMD1, 0, 0X01); // Send CMD1
				} while (r1 && retry--);
			}
			if (retry == 0 || SD_sendcmd(CMD16, 512, 0X01) != 0)
			{
				SD_TYPE = ERR; // Wrong card
//				printf("SD_TYPE: Error   \r\n");
			}
		}
	}
	SD_CS(0);

	switch (SD_TYPE)
	{
	case V2HC:
		SD_SPIx_SetSpeed(SPI_BaudRatePrescaler_4);
		break;
	case V2:
		SD_SPIx_SetSpeed(SPI_BaudRatePrescaler_4);
		break;
	case V1:
		SD_SPIx_SetSpeed(SPI_BaudRatePrescaler_20);
		break;
	case MMC:
		SD_SPIx_SetSpeed(SPI_BaudRatePrescaler_20);
		break;
	default:
		break;
	}

	if (SD_TYPE)
		return 0;
	else
		return 1;
}

// Get CID information
uint8_t SD_GETCID(uint8_t *cid_data)
{
	uint8_t r1;
	r1 = SD_sendcmd(CMD10, 0, 0x01); // Read CID register
	if (r1 == 0x00)
	{
		r1 = SD_ReceiveData(cid_data, 16);
	}
	SD_CS(0);
	if (r1)
		return 1;
	else
		return 0;
}
// Get CSD information
uint8_t SD_GETCSD(uint8_t *csd_data)
{
	uint8_t r1;
	r1 = SD_sendcmd(CMD9, 0, 0x01); // Send CMD9, read the CSD register
	if (r1 == 0)
	{
		r1 = SD_ReceiveData(csd_data, 16); // Receive 16 bytes of data
	}
	SD_CS(0); // Cancel film selection
	if (r1)
		return 1;
	else
		return 0;
}
// Get the total number of sectors of the SD card
uint32_t SD_GetSectorCount(void)
{
	uint8_t csd[16];
	uint32_t Capacity;
	uint8_t n;
	uint16_t csize;
	// Get CSD information. If there is an error during the period, return 0
	if (SD_GETCSD(csd) != 0)
		return 0;
	// If it is an SDHC card, it is calculated as follows
	if ((csd[0] & 0xC0) == 0x40) // V2.00 SDcard
	{
		csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
		Capacity = (uint32_t)csize << 10; // Get the number of sectors
	}
	else // V1.XX SDcard
	{
		n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
		csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
		Capacity = (uint32_t)csize << (n - 9); // Get the number of sectors
	}
	return Capacity;
}
int32_t MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo)
{
	uint8_t r1;
	uint8_t CSD_Tab[16];
	uint8_t CID_Tab[16];

	/* Send CMD9, Read CSD */
	r1 = SD_sendcmd(CMD9, 0, 0xFF);
	if (r1 != 0x00)
	{
		return r1;
	}

	if (SD_ReceiveData(CSD_Tab, 16))
	{
		return 1;
	}

	/* Send CMD10, Read CID */
	r1 = SD_sendcmd(CMD10, 0, 0xFF);
	if (r1 != 0x00)
	{
		return r1;
	}

	if (SD_ReceiveData(CID_Tab, 16))
	{
		return 2;
	}

	/* Byte 0 */
	SD0_CardInfo->CSD.CSDStruct = (CSD_Tab[0] & 0xC0) >> 6;
	SD0_CardInfo->CSD.SysSpecVersion = (CSD_Tab[0] & 0x3C) >> 2;
	SD0_CardInfo->CSD.Reserved1 = CSD_Tab[0] & 0x03;
	/* Byte 1 */
	SD0_CardInfo->CSD.TAAC = CSD_Tab[1];
	/* Byte 2 */
	SD0_CardInfo->CSD.NSAC = CSD_Tab[2];
	/* Byte 3 */
	SD0_CardInfo->CSD.MaxBusClkFrec = CSD_Tab[3];
	/* Byte 4 */
	SD0_CardInfo->CSD.CardComdClasses = CSD_Tab[4] << 4;
	/* Byte 5 */
	SD0_CardInfo->CSD.CardComdClasses |= (CSD_Tab[5] & 0xF0) >> 4;
	SD0_CardInfo->CSD.RdBlockLen = CSD_Tab[5] & 0x0F;
	/* Byte 6 */
	SD0_CardInfo->CSD.PartBlockRead = (CSD_Tab[6] & 0x80) >> 7;
	SD0_CardInfo->CSD.WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6;
	SD0_CardInfo->CSD.RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5;
	SD0_CardInfo->CSD.DSRImpl = (CSD_Tab[6] & 0x10) >> 4;
	SD0_CardInfo->CSD.Reserved2 = 0; /* Reserved */
	SD0_CardInfo->CSD.DeviceSize = (CSD_Tab[6] & 0x03) << 10;
	/* Byte 7 */
	SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[7]) << 2;
	/* Byte 8 */
	SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[8] & 0xC0) >> 6;
	SD0_CardInfo->CSD.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3;
	SD0_CardInfo->CSD.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07);
	/* Byte 9 */
	SD0_CardInfo->CSD.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5;
	SD0_CardInfo->CSD.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2;
	SD0_CardInfo->CSD.DeviceSizeMul = (CSD_Tab[9] & 0x03) << 1;
	/* Byte 10 */
	SD0_CardInfo->CSD.DeviceSizeMul |= (CSD_Tab[10] & 0x80) >> 7;
	SD0_CardInfo->CSD.EraseGrSize = (CSD_Tab[10] & 0x7C) >> 2;
	SD0_CardInfo->CSD.EraseGrMul = (CSD_Tab[10] & 0x03) << 3;
	/* Byte 11 */
	SD0_CardInfo->CSD.EraseGrMul |= (CSD_Tab[11] & 0xE0) >> 5;
	SD0_CardInfo->CSD.WrProtectGrSize = (CSD_Tab[11] & 0x1F);
	/* Byte 12 */
	SD0_CardInfo->CSD.WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7;
	SD0_CardInfo->CSD.ManDeflECC = (CSD_Tab[12] & 0x60) >> 5;
	SD0_CardInfo->CSD.WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2;
	SD0_CardInfo->CSD.MaxWrBlockLen = (CSD_Tab[12] & 0x03) << 2;
	/* Byte 13 */
	SD0_CardInfo->CSD.MaxWrBlockLen |= (CSD_Tab[13] & 0xc0) >> 6;
	SD0_CardInfo->CSD.WriteBlockPaPartial = (CSD_Tab[13] & 0x20) >> 5;
	SD0_CardInfo->CSD.Reserved3 = 0;
	SD0_CardInfo->CSD.ContentProtectAppli = (CSD_Tab[13] & 0x01);
	/* Byte 14 */
	SD0_CardInfo->CSD.FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7;
	SD0_CardInfo->CSD.CopyFlag = (CSD_Tab[14] & 0x40) >> 6;
	SD0_CardInfo->CSD.PermWrProtect = (CSD_Tab[14] & 0x20) >> 5;
	SD0_CardInfo->CSD.TempWrProtect = (CSD_Tab[14] & 0x10) >> 4;
	SD0_CardInfo->CSD.FileFormat = (CSD_Tab[14] & 0x0C) >> 2;
	SD0_CardInfo->CSD.ECC = (CSD_Tab[14] & 0x03);
	/* Byte 15 */
	SD0_CardInfo->CSD.CSD_CRC = (CSD_Tab[15] & 0xFE) >> 1;
	SD0_CardInfo->CSD.Reserved4 = 1;

	if (SD0_CardInfo->CardType == V2HC)
	{
		/* Byte 7 */
		SD0_CardInfo->CSD.DeviceSize = (uint16_t)(CSD_Tab[8]) * 256;
		/* Byte 8 */
		SD0_CardInfo->CSD.DeviceSize += CSD_Tab[9];
	}

	SD0_CardInfo->Capacity = SD0_CardInfo->CSD.DeviceSize * MSD_BLOCKSIZE * 1024;
	SD0_CardInfo->BlockSize = MSD_BLOCKSIZE;

	/* Byte 0 */
	SD0_CardInfo->CID.ManufacturerID = CID_Tab[0];
	/* Byte 1 */
	SD0_CardInfo->CID.OEM_AppliID = CID_Tab[1] << 8;
	/* Byte 2 */
	SD0_CardInfo->CID.OEM_AppliID |= CID_Tab[2];
	/* Byte 3 */
	SD0_CardInfo->CID.ProdName1 = CID_Tab[3] << 24;
	/* Byte 4 */
	SD0_CardInfo->CID.ProdName1 |= CID_Tab[4] << 16;
	/* Byte 5 */
	SD0_CardInfo->CID.ProdName1 |= CID_Tab[5] << 8;
	/* Byte 6 */
	SD0_CardInfo->CID.ProdName1 |= CID_Tab[6];
	/* Byte 7 */
	SD0_CardInfo->CID.ProdName2 = CID_Tab[7];
	/* Byte 8 */
	SD0_CardInfo->CID.ProdRev = CID_Tab[8];
	/* Byte 9 */
	SD0_CardInfo->CID.ProdSN = CID_Tab[9] << 24;
	/* Byte 10 */
	SD0_CardInfo->CID.ProdSN |= CID_Tab[10] << 16;
	/* Byte 11 */
	SD0_CardInfo->CID.ProdSN |= CID_Tab[11] << 8;
	/* Byte 12 */
	SD0_CardInfo->CID.ProdSN |= CID_Tab[12];
	/* Byte 13 */
	SD0_CardInfo->CID.Reserved1 |= (CID_Tab[13] & 0xF0) >> 4;
	/* Byte 14 */
	SD0_CardInfo->CID.ManufactDate = (CID_Tab[13] & 0x0F) << 8;
	/* Byte 15 */
	SD0_CardInfo->CID.ManufactDate |= CID_Tab[14];
	/* Byte 16 */
	SD0_CardInfo->CID.CID_CRC = (CID_Tab[15] & 0xFE) >> 1;
	SD0_CardInfo->CID.Reserved2 = 1;

	return 0;
}

// Write SD card
// buf:Data buffer
// sector:starting sector
// cnt:sector number
// return:0,ok;others,failure.
uint8_t SD_WriteDisk(uint8_t *buf, uint32_t sector, uint8_t cnt)
{
	uint8_t r1;
	if (SD_TYPE != V2HC)
		sector *= 512; // Convert to byte address
	if (cnt == 1)
	{
		r1 = SD_sendcmd(CMD24, sector, 0X01); // Read command
		if (r1 == 0)						  // Command sent successfully
		{
			r1 = SD_SendBlock(buf, 0xFE); // Write 512 bytes
		}
	}
	else
	{
		if (SD_TYPE != MMC)
		{
			SD_sendcmd(CMD55, 0, 0X01);
			SD_sendcmd(CMD23, cnt, 0X01); // Send instructions
		}
		r1 = SD_sendcmd(CMD25, sector, 0X01); // Continuous read command
		if (r1 == 0)
		{
			do
			{
				r1 = SD_SendBlock(buf, 0xFC); // Receive 512 bytes
				buf += 512;
			} while (--cnt && r1 == 0);
			r1 = SD_SendBlock(0, 0xFD);
		}
	}
	SD_CS(0);  // Cancel CS
	return r1;
}
// Read SD card
// buf:Data buffer
// sector:sector address
// cnt:sector number
// return:0,ok;others,failure.
uint8_t SD_ReadDisk(uint8_t *buf, uint32_t sector, uint8_t cnt)
{
	uint8_t r1;
	if (SD_TYPE != V2HC)
		sector <<= 9; // Convert to byte address
	if (cnt == 1)
	{
		r1 = SD_sendcmd(CMD17, sector, 0X01); // Read command
		if (r1 == 0)						  // Command sent successfully
		{
			r1 = SD_ReceiveData(buf, 512); // Receive 512 bytes
		}
	}
	else
	{
		r1 = SD_sendcmd(CMD18, sector, 0X01);	//Continuous read command
		do
		{
			r1 = SD_ReceiveData(buf, 512); // Receive 512 bytes
			buf += 512;
		} while (--cnt && r1 == 0);
		SD_sendcmd(CMD12, 0, 0X01); // Send stop command
	}
	SD_CS(0);	// Cancel CS
	return r1; 
}

void Get_SDCard_Capacity(void)
{
	FRESULT result;
	FATFS FS;
	FATFS *fs;
	DWORD fre_clust, AvailableSize, UsedSize;
	uint16_t TotalSpace;
	uint8_t res;

	//	res = SD_init();		//SD card initialization
	//	if(res == 1)
	//	{
	//		printf("SD card initialization failed! \r\n");
	//	}
	//	else
	//	{
	//		printf("SD card initialization succeeded! \r\n");
	//	}

	/* mount */
	res = f_mount(&FS, "0:", 1);
	if (res != FR_OK)
	{
//		printf("FileSystem Mounted Failed (%d)\r\n", res);
	}

	res = f_getfree("0:", &fre_clust, &fs); /* root directory */
	if (res == FR_OK)
	{
		TotalSpace = (uint16_t)(((fs->n_fatent - 2) * fs->csize) / 2 / 1024);
		AvailableSize = (uint16_t)((fre_clust * fs->csize) / 2 / 1024);
		UsedSize = TotalSpace - AvailableSize;
		/* Print free space in unit of MB (assuming 512 bytes/sector) */
//		printf("\r\n%d MB total drive space.\r\n"
//			   "%d MB available.\r\n"
//			   "%d MB  used.\r\n",
//			   TotalSpace, AvailableSize, UsedSize);
	}
	else
	{
//		printf("Get SDCard Capacity Failed (%d)\r\n", result);
	}
}
